﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Odyssey.ViewModels;
using System.Collections.ObjectModel;
using System.Linq;
using System.Globalization;

namespace Odyssey.Controls.Models
{
    public class DateItem : ViewModelBase
    {

        public DateItem(int value, string text, string details = "")
            : base()
        {
            this.Text = text;
            this.details = details;
            this.Value = value;
        }
        private string details;

        public int Value { get; private set; }

        public string Details
        {
            get { return details; }
            set
            {
                if (details != value)
                {
                    details = value;
                    OnPropertyChanged("Details");
                }
            }
        }

        public string Text { get; set; }

    }

    public class DatePickerModel : ModelBase
    {
        const int startYear = 1900;

        static DatePickerModel()
        {
            dateTimeFormat = CultureInfo.CurrentCulture.DateTimeFormat;
            years = Enumerable.Range(startYear, 2100).Select(y => new DateItem(y, y.ToString(), " ")).ToArray();
            months = Enumerable.Range(1, 12).Select(m => new DateItem(m, m.ToString("D2"), dateTimeFormat.MonthNames[m - 1])).ToArray();

            DayOrderIndex = GetDayIndex();
            MonthOrderIndex = GetMonthIndex();
            YearOrderIndex = GetYearIndex();
        }

        public DatePickerModel()
            : base()
        {
            Days = new ObservableCollection<DateItem>();
        }

        private static DateTimeFormatInfo dateTimeFormat;
        private static DateItem[] months;
        private static DateItem[] years;
        private DateTime? date;

        public static int DayOrderIndex { get; private set; }
        public static int MonthOrderIndex { get; private set; }
        public static int YearOrderIndex { get; private set; }

        public ObservableCollection<DateItem> Days { get; private set; }
        public DateItem[] Months { get { return months; } }
        public DateItem[] Years { get { return years; } }

        /// <summary>
        /// Gets or sets the selected date.
        /// </summary>
        public DateTime Date
        {
            get
            {
                if (date == null) Date = DateTime.Now;
                return date.Value;
            }
            set
            {
                if (date != value)
                {
                    date = value;
                    OnPropertyChanged("Date");
                    OnDateChanged();
                }
            }
        }

        private DateItem selectedYear;
        private DateItem selectedMonth;
        private DateItem selectedDay;

        public DateItem SelectedDay
        {
            get { return selectedDay; }
            set
            {
                if (selectedDay != value)
                {
                    selectedDay = value;
                    OnPropertyChanged("SelectedDay");
                    if (value != null) UpdateDate();
                }
            }
        }

        public DateItem SelectedMonth
        {
            get { return selectedMonth; }
            set
            {
                if (selectedMonth != value)
                {
                    selectedMonth = value;
                    OnPropertyChanged("SelectedMonth");
                    if (value != null) UpdateDays();
                }
            }
        }

        public DateItem SelectedYear
        {
            get { return selectedYear; }
            set
            {
                if (selectedYear != value)
                {
                    selectedYear = value;
                    OnPropertyChanged("SelectedYear");
                    if (value != null) UpdateDays();
                }
            }
        }

        /// <summary>
        /// Gets the selected year.
        /// </summary>
        protected int Year
        {
            get { return selectedYear != null ? selectedYear.Value : 1900; }
        }

        /// <summary>
        /// Gets the selected month between 1 and 12.
        /// </summary>
        protected int Month
        {
            get { return selectedMonth != null ? selectedMonth.Value : 1; }
        }

        /// <summary>
        /// Gets the selected day between 1 and 31.
        /// </summary>
        protected int Day
        {
            get { return selectedDay != null ? selectedDay.Value : 1; }
        }

        private void UpdateDays()
        {
            if (selectedMonth != null && selectedYear != null)
            {
                int year = Year;
                int month = Month;

                int daysInMonth = DateTime.DaysInMonth(year, month);
                while (Days.Count > daysInMonth) Days.RemoveAt(Days.Count - 1);
                for (int i = Days.Count; i < daysInMonth; i++)
                {
                    int dayIndex = i + 1;
                    DateTime dt = new DateTime(year, month, dayIndex);
                    Days.Add(new DateItem(dayIndex, dayIndex.ToString("D2"), dateTimeFormat.GetDayName(dt.DayOfWeek)));
                }
                foreach (var item in Days)
                {
                    DateTime dt = new DateTime(year, month, item.Value);
                    item.Details = dateTimeFormat.GetDayName(dt.DayOfWeek);
                }
                if (!Days.Contains(selectedDay))
                {
                    SelectedDay = Days[Days.Count - 1];
                }
                UpdateDate();
            }
        }

        private static int GetDayIndex()
        {
            string pattern = dateTimeFormat.ShortDatePattern;
            int m = pattern.IndexOf("m", StringComparison.InvariantCultureIgnoreCase);
            int y = pattern.IndexOf("y", StringComparison.InvariantCultureIgnoreCase);
            int d = pattern.IndexOf("d", StringComparison.InvariantCultureIgnoreCase);

            if (d < y && d < m) return 0;
            return d < y || d < m ? 1 : 2;
        }

        private static int GetYearIndex()
        {
            string pattern = dateTimeFormat.ShortDatePattern;
            int m = pattern.IndexOf("m", StringComparison.InvariantCultureIgnoreCase);
            int y = pattern.IndexOf("y", StringComparison.InvariantCultureIgnoreCase);
            int d = pattern.IndexOf("d", StringComparison.InvariantCultureIgnoreCase);
            if (y < m && y < d) return 0;
            return y < m || y < d ? 1 : 2;
        }

        private static int GetMonthIndex()
        {
            string pattern = dateTimeFormat.ShortDatePattern;
            int m = pattern.IndexOf("m", StringComparison.InvariantCultureIgnoreCase);
            int y = pattern.IndexOf("y", StringComparison.InvariantCultureIgnoreCase);
            int d = pattern.IndexOf("d", StringComparison.InvariantCultureIgnoreCase);
            if (m < y && m < d) return 0;
            return m < y || m < d ? 1 : 2;
        }


        private void UpdateDate()
        {
            if (selectedDay != null && selectedYear != null && selectedMonth != null)
            {
                Date = new DateTime(Year, Month, Day);
            }
        }


        protected void OnDateChanged()
        {
            DateTime date = this.date.Value;
            SelectedMonth = Months[date.Month - 1];
           SelectedYear = Years[date.Year - startYear];
            UpdateDays();
            SelectedDay = Days[date.Day - 1];
            var eh = DateChanged;
            if (eh != null) eh(this, EventArgs.Empty);
        }

        public event EventHandler DateChanged;
    }

}
